

#include "unit.h"
#include "bullet.h"
#include "animation.h"
#include "rallypoint.h"
#include "gameplayscene.h"
#include "resourcemanager.h"
#include "gamedatamanager.h"
#include "gamemanager.h"

int Unit::seed = 1;

static const int LIFEBAR_OFFSET_H = 100;

Unit::Unit() : 
	sprite(nullptr)
	, gamePlayScene(nullptr)
	, unitType(UnitType::ground)
	, state(UnitState::stand)
	, previousState(UnitState::stand)
	, target(nullptr)
	, myFlag(nullptr)
	, hp(0.0f)
	, level(0) 
	, _ticks(0.0f)
	, _sprLifebar(nullptr)
	, _sprLifebarBg(nullptr)
	, _labelDebug(nullptr)
{
	id = seed++;
	position = Point::ZERO;
	speedStep = Point::ZERO;
	speed = Point::ZERO;
	targetPosition = Point::ZERO;

	memset(buf, 0, sizeof(buf));
}

Unit::~Unit() {
	if(sprite) {
		sprite->removeFromParent();
	}

	clearBullets();
}

void Unit::set(const string& name, const Point& pos, GamePlayScene* scene){
	gamePlayScene = scene;

	if(sprite) {
		sprite->removeFromParent();
		sprite = nullptr;
	}

	position = pos;
	unitName = name;

	if(GDM->unitsAttribute.find(unitName) == GDM->unitsAttribute.end()) {
		log("invalid unit name:%s", unitName.c_str());
	}

	setAttribute(GDM->unitsAttribute[unitName]);

	sprite = CCSkeletalSprite::create(name.c_str());
	sprite->setScale(0.5f);
	sprite->setLogicPosition(position);
	sprite->playAnimation("idle");
	sprite->setLoopCount(-1);
	gamePlayScene->addSceneNode(sprite);

	_sprLifebarBg = CREATE_SPRITE("textures/units/lifebar.png");
	_sprLifebarBg->setScale(2.0f);
	_sprLifebarBg->setColor(Color3B(48,48,48));
	_sprLifebarBg->setPosition(Point(0.0f, LIFEBAR_OFFSET_H));

	Size size = _sprLifebarBg->getContentSize();

	Sprite* sprBar = CREATE_SPRITE("textures/units/lifebar.png");
	sprBar->setScale(1.0f);
	_sprLifebar = ProgressTimer::create(sprBar);
	_sprLifebar->setType(ProgressTimer::Type::BAR);
	_sprLifebar->setMidpoint(Point(0.0f, sprBar->getContentSize().height / 2.0f));
	_sprLifebar->setPosition(Point::ZERO); 
	_sprLifebar->setAnchorPoint(Point::ZERO);
	_sprLifebar->setScale(1.0f);
	_sprLifebar->setColor(Color3B::GREEN);
	_sprLifebar->setPercentage(100.0f);

	_sprLifebarBg->addChild(_sprLifebar);
	sprite->addChild(_sprLifebarBg);

	sprite->registerHandler(std::bind(&Unit::onAttack, this, std::placeholders::_1));

	_labelDebug = LabelTTF::create("", "", 32);
	_labelDebug->setColor(Color3B::BLUE);
	_labelDebug->setVisible(false);
	sprite->addChild(_labelDebug);

	transformState(UnitState::walk_to_flag);
}

void Unit::transformState(UnitState newState) {
	handleExitState();
	previousState = state;
	state = newState;
	handleEnterState();
}

void Unit::handleEnterState() {
	switch (state)
	{
	case UnitState::stand:
		sprite->playAnimation("idle");
		sprite->setLoopCount(-1);
		break;
	case UnitState::search:
		break;
	case UnitState::walk_to_flag:
	case UnitState::head_to_fight:
	case UnitState::walk_to_mining:
	case UnitState::back_to_camp:
		sprite->playAnimation("walk");
		sprite->setLoopCount(-1);
		break;
	case UnitState::melee:
		sprite->playAnimation("attack");
		sprite->setLoopCount(1);
		break;
	case UnitState::shoot:
		sprite->playAnimation("shoot");
		sprite->setLoopCount(1);
		break;
	case UnitState::cast:
		sprite->playAnimation("cast");
		sprite->setLoopCount(1);
		break;
	case UnitState::reload:
		sprite->playAnimation("idle");
		sprite->setLoopCount(1);
		break;
	case UnitState::dying:
		sprite->playAnimation("die");
		sprite->setLoopCount(1);
		break;
	case UnitState::birth:
		break;
	case UnitState::freeze:
		break;
	case UnitState::work:
		sprite->playAnimation("work");
		sprite->setLoopCount(-1);
		break;
	case UnitState::dizzy:
		break;
	default:
		break;
	}
}

void Unit::handleExitState() {
	switch(state) {
	case UnitState::reload:
		_ticks = 0.0f;
		break;
	default:break;
	}
}

void Unit::updateBullets(float ticks) {
	for (list<Bullet*>::iterator ite = bullets.begin(); ite != bullets.end();) {
		Bullet* bullet = *ite;
		if(!bullet->update(ticks)) {
			ite = bullets.erase(ite);

			if(bullet->isHitTarget) {
				handleBulletHit(bullet);
			}

			delete bullet;

		} else {
			++ite;
		}
	}
}

bool Unit::move(float ticks)  {
	bool ret = true;

	speedStep = speed * ticks;
	position = position + speedStep;

	if(targetPosition.getDistance(position) < speedStep.getLength()) {
		position = targetPosition;
		ret = false;
	}

	sprite->setLogicPosition(position);

	return ret;
}

Point Unit::getStrikePoint(){
	return sprite->getBonePositionRelativeToWorld("head");
}

void Unit::clearBullets(){
	for( auto bullet : bullets) {
		bullet->target = nullptr;
		gamePlayScene->bullets.push_back(bullet);
	}

	bullets.clear();
}

void Unit::moveTo(const Point& pos) {
	targetPosition = pos;

	float angle = (targetPosition - position).getAngle();
	speed = (Point(attribute.speed, 0)).rotateByAngle(Point::ZERO, angle);

	sprite->setFlipX(targetPosition.x < position.x);
}

void Unit::handleBulletHit(Bullet* bullet, float damageFactor) {
	if(!isAlive()) {
		return;
	}

	hp -= bullet->meleeDamage * damageFactor;
	hp -= bullet->rangedDamage * damageFactor;

	if (hp > 0) {
		_sprLifebar->setPercentage(hp / attribute.health * 100.0f);
	} else {
		hp = 0.0f;
		transformState(UnitState::dying);
	}

	if(bullet->type == BulletType::piercing) {
		AnimationInfo info = RM->animationInfos["spurting_blood"];
		float delay = info.frames.size() * info.frameRate;

		Point pos = sprite->getBonePositionRelativeToWorld("head");
		FrameAnimation* animBloodSpurtingFx = RM->createAnimation("spurting_blood", pos);
		animBloodSpurtingFx->setZOrder(999);
		animBloodSpurtingFx->setTag(TAG_NO_ZORDER);
		animBloodSpurtingFx->isLoop = false;
		gamePlayScene->addFrameAnimations(animBloodSpurtingFx);
	}
}

float Unit::getClosedFightRange() {
	return sprite->getBoundingBox().size.width / 2.0f;
}

bool Unit::isInsideEllipse(const Point& pos, float range) {
	if (range <= 0.0f){
		return false;
	}

	float ew = range + range;
	float eh = ew * 0.7f;
	float ex = pos.x - range;
	float ey = pos.y - eh / 2.0f;

	/*float dw = _roleInfo.bound.size.width / 2;

	float arrX[4] = {_location.x - dw, _location.x - dw, _location.x + dw, _location.x + dw};
	float arrY[4] = {_location.y + _roleInfo.bound.size.height, _location.y, _location.y + _roleInfo.bound.size.height, _location.y};

	for(int i = 0; i < 4; i++) {

	float x = arrX[i];
	float y = arrY[i];

	float normx = (x - ex) / ew - 0.5f;
	float normy = (y - ey) / eh - 0.5f;
	*/

	float normx = (position.x - ex) / ew - 0.5f;
	float normy = (position.y - ey) / eh - 0.5f;
	bool ret = (normx * normx + normy * normy) < 0.25f;

	if(ret) {
		return true;
	}
	//}

	return false;
}

bool Unit::isInsideBound(const Point& pt) {
	return false;
}

Point Unit::getEstimatedPosition(float ticks) {
	Point pos = position;

	if(state == UnitState::walk_to_flag) {
		Point dist = getSpeed() * ticks;
		pos = pos + dist;
	}

	return pos;
}

Point Unit::getEstimatedStrikePoint(float ticks) {
	Point pos = getStrikePoint();

	if(state == UnitState::walk_to_flag) {
		Point dist = getSpeed() * ticks;
		pos = pos + dist;
	}

	return pos;
}

void Unit::onAttack( EventData* event )
{

}
